{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'3.2.0'"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import triton\n",
    "import triton.backends\n",
    "import triton.language as tl\n",
    "import math\n",
    "import os\n",
    "os.environ['TRITON_PRINT_AUTOTUNING'] = '1'\n",
    "triton.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# sglang\n",
    "- 去凑成MQA的形式，num_kv_head = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# def forward_absorb(self,positions: torch.Tensor,hidden_states: torch.Tensor,forward_batch: ForwardBatch) -> torch.Tensor:\n",
    "#     q_len = hidden_states.shape[0]\n",
    "#     q_input = hidden_states.new_empty(q_len, self.num_local_heads, self.kv_lora_rank + self.qk_rope_head_dim)\n",
    "    \n",
    "#     q = self.q_a_proj(hidden_states)[0]\n",
    "#     q = self.q_a_layernorm(q)\n",
    "#     q = self.q_b_proj(q)[0].view(-1, self.num_local_heads, self.qk_head_dim)\n",
    "#     q_nope, q_pe = q.split([self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)\n",
    "#     # 把q_nope吸收w_kc，类似传统mha的 Q @ K^T  =  X @ Wq @ (X @ Wk)^T  =  (X @ Wq @ Wk^T) @ X^T\n",
    "#     q_nope_out = torch.bmm(q_nope.transpose(0, 1), self.w_kc) \n",
    "#     q_input[..., : self.kv_lora_rank] = q_nope_out.transpose(0, 1)# q的前一部分包括吸了w_kc的q_nope1\n",
    "\n",
    "#     latent_cache = self.kv_a_proj_with_mqa(hidden_states)[0] \n",
    "#     v_input = latent_cache[..., : self.kv_lora_rank] # hf中的compressed_kv, weight @ V = weight @ X @ w_v，相当于把x拿出来当v\n",
    "#     v_input = self.kv_a_layernorm(v_input.contiguous()).unsqueeze(1) # 加一个head维度\n",
    "#     k_input = latent_cache.unsqueeze(1)   # 加一个head维度\n",
    "#     k_input[..., : self.kv_lora_rank] = v_input  # 把前面替换为经过norm的compressed_kv\n",
    "#     k_pe = k_input[..., self.kv_lora_rank :]\n",
    "\n",
    "#     q_pe, k_pe = self.rotary_emb(positions, q_pe, k_pe)\n",
    "#     q_input[..., self.kv_lora_rank :] = q_pe   # 替换q和k带rope的地方\n",
    "#     k_input[..., self.kv_lora_rank :] = k_pe\n",
    "\n",
    "#     # MQA，attn_mqa中forward的时候，会把k_input给cache起来，包括compressed_kv和k_pe, v_input不需要cache\n",
    "#     attn_output = self.attn_mqa(q_input, k_input, v_input, forward_batch)\n",
    "#     attn_output = attn_output.view(-1, self.num_local_heads, self.kv_lora_rank)\n",
    "\n",
    "#     attn_bmm_output = torch.bmm(attn_output.transpose(0, 1), self.w_vc)  # 乘上开始没乘的矩阵w_v\n",
    "#     attn_output = attn_bmm_output.transpose(0, 1).flatten(1, 2) \n",
    "#     output, _ = self.o_proj(attn_output)\n",
    "\n",
    "#     return output"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# torch code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def torch_mqa(q:torch.Tensor, k:torch.Tensor, v:torch.Tensor, scale=None, attention_mask:torch.Tensor=None):\n",
    "    attn_score = torch.matmul(q, k.transpose(-1, -2)) * scale\n",
    "    B, H, N, _ = q.shape\n",
    "    M = k.size(-2)\n",
    "    if N > 1:\n",
    "        assert N == M\n",
    "        causal_mask = torch.ones(N, M, device=q.device).tril()[None, None, :, :].bool()\n",
    "        if attention_mask is not None:\n",
    "            attention_mask = causal_mask & attention_mask[:, None, None, :].bool()\n",
    "        else:\n",
    "            attention_mask = causal_mask\n",
    "        attn_score.masked_fill_(~attention_mask, -65000)\n",
    "    else:\n",
    "        if attention_mask is not None:\n",
    "            attention_mask = attention_mask[:, None, None, :].bool()\n",
    "            attn_score.masked_fill_(~attention_mask, -65000)\n",
    "    attn_weight = torch.nn.functional.softmax(attn_score, dim=-1, dtype=torch.float32).to(q.dtype)\n",
    "    out = torch.matmul(attn_weight, v)\n",
    "    return out"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def torch_mqa(q:torch.Tensor, k:torch.Tensor, v:torch.Tensor, scale=None, attention_mask:torch.Tensor=None, decode_one_by_one=False):\n",
    "    B, H, N, _ = q.shape\n",
    "    M = k.size(-2)\n",
    "    if N > 1:\n",
    "        assert N == M\n",
    "        attn_score = torch.matmul(q, k.transpose(-1, -2)) * scale\n",
    "        causal_mask = torch.ones(N, M, device=q.device).tril()[None, None, :, :].bool()\n",
    "        if attention_mask is not None:\n",
    "            attention_mask = causal_mask & attention_mask[:, None, None, :].bool()\n",
    "        else:\n",
    "            attention_mask = causal_mask\n",
    "        attn_score.masked_fill_(~attention_mask, -65000)\n",
    "        attn_weight = torch.nn.functional.softmax(attn_score, dim=-1, dtype=torch.float32).to(q.dtype)\n",
    "        out = torch.matmul(attn_weight, v)\n",
    "        \n",
    "    else:\n",
    "        if attention_mask is not None:\n",
    "            attention_mask = attention_mask[:, None, None, :].bool()\n",
    "\n",
    "        if decode_one_by_one:\n",
    "            out = torch.empty(B, H, N, v.size(-1), device=q.device, dtype=q.dtype)\n",
    "            for idx in range(B):\n",
    "                attn_score = torch.matmul(q[idx], k[idx].transpose(-1, -2)) * scale\n",
    "                if attention_mask is not None:\n",
    "                    attn_score.masked_fill_(~attention_mask[idx], -65000)\n",
    "                attn_weight = torch.nn.functional.softmax(attn_score, dim=-1, dtype=torch.float32).to(q.dtype)\n",
    "                out[idx] = torch.matmul(attn_weight, v[idx])\n",
    "\n",
    "        else:\n",
    "            attn_score = torch.matmul(q, k.transpose(-1, -2)) * scale\n",
    "            if attention_mask is not None:\n",
    "                attention_mask = attention_mask[:, None, None, :].bool()\n",
    "                attn_score.masked_fill_(~attention_mask, -65000)\n",
    "            attn_weight = torch.nn.functional.softmax(attn_score, dim=-1, dtype=torch.float32).to(q.dtype)\n",
    "            out = torch.matmul(attn_weight, v)\n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# triton code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @triton.autotune([triton.Config({'BLOCK_SIZE_N': bsn, 'BLOCK_SIZE_M': bsm}, num_stages=ns, num_warps=nw)\n",
    "#                  for bsm in [32, 64]\n",
    "#                  for bsn in [32, 64]\n",
    "#                  for ns in [2, 4]\n",
    "#                  for nw in [4, 8]\n",
    "#                  ], key=['N', 'KV_LORA_RANK', 'ROPE_HEAD_DIM'])\n",
    "@triton.jit\n",
    "def _mla_encode_kernel(Q, K, V, OUT, \n",
    "                    NUM_PADS, SCALE,\n",
    "                    q_stride_b, q_stride_h, q_stride_n, q_stride_d,\n",
    "                    k_stride_b, k_stride_m, k_stride_d,\n",
    "                    v_stride_b, v_stride_m, v_stride_d,\n",
    "                    out_stride_b, out_stride_h, out_stride_n, out_stride_d,\n",
    "                    N, KV_LORA_RANK: tl.constexpr, ROPE_HEAD_DIM: tl.constexpr,\n",
    "                    BLOCK_SIZE_N: tl.constexpr, BLOCK_SIZE_M: tl.constexpr\n",
    "                    ):\n",
    "    off_b = tl.cast(tl.program_id(0), tl.int64)\n",
    "    off_h = tl.cast(tl.program_id(1), tl.int64)\n",
    "    off_n = tl.cast(tl.program_id(2), tl.int64) * BLOCK_SIZE_N\n",
    "\n",
    "    \n",
    "    NUM_PADS += off_b\n",
    "    num_pads = tl.cast(tl.load(NUM_PADS), tl.int64)\n",
    "\n",
    "    if (off_n + BLOCK_SIZE_N) > num_pads:\n",
    "        Q += off_b * q_stride_b + off_h * q_stride_h + off_n * q_stride_n\n",
    "        K += off_b * k_stride_b + num_pads * k_stride_m\n",
    "        V += off_b * v_stride_b\n",
    "        OUT += off_b * out_stride_b + off_h * out_stride_h + off_n * out_stride_n\n",
    "\n",
    "        nn = tl.arange(0, BLOCK_SIZE_N)\n",
    "        mm = tl.arange(0, BLOCK_SIZE_M)\n",
    "        \n",
    "        dtype = Q.type.element_ty\n",
    "    \n",
    "        q_nope_ptrs = Q + nn[:, None] * q_stride_n + tl.arange(0, KV_LORA_RANK)[None, :]\n",
    "        q_rope_ptrs = Q + nn[:, None] * q_stride_n + tl.arange(0, ROPE_HEAD_DIM)[None, :] + KV_LORA_RANK\n",
    "        k_nope_ptrs = K + mm[None, :] * k_stride_m + tl.arange(0, KV_LORA_RANK)[:, None]\n",
    "        k_rope_ptrs = K + mm[None, :] * k_stride_m + tl.arange(0, ROPE_HEAD_DIM)[:, None] + KV_LORA_RANK\n",
    "        \n",
    "        mask_n = (off_n + nn) < N\n",
    "        q_nope = tl.load(q_nope_ptrs, mask=mask_n[:, None], other=0.) # q_nope是q的前kv_lora_rank维度\n",
    "        q_rope = tl.load(q_rope_ptrs, mask=mask_n[:, None], other=0.)\n",
    "        acc = tl.zeros((BLOCK_SIZE_N, KV_LORA_RANK), dtype=tl.float32)\n",
    "        m_i = tl.zeros((BLOCK_SIZE_N,), dtype=tl.float32) - float('inf')\n",
    "        l_i = tl.zeros((BLOCK_SIZE_N,), dtype=tl.float32)\n",
    "        for start_m in range(num_pads, off_n + BLOCK_SIZE_N, BLOCK_SIZE_M): \n",
    "            mask_m = (start_m + mm) < N\n",
    "            k_nope = tl.load(k_nope_ptrs, mask=mask_m[None, :], other=0.)\n",
    "            k_rope = tl.load(k_rope_ptrs, mask=mask_m[None, :], other=0.) # k_nope是k的前kv_lora_rank维度\n",
    "\n",
    "            attn_score= tl.dot(q_rope, k_rope)\n",
    "            attn_score = tl.dot(q_nope, k_nope, acc=attn_score) * SCALE\n",
    "            \n",
    "            attn_mask = (off_n + nn)[:, None] >= (start_m + mm)[None, :]\n",
    "            attn_score = tl.where(attn_mask & mask_m[None, :], attn_score, -60000)\n",
    "\n",
    "            m_ij = tl.max(attn_score, axis=1)\n",
    "            new_m_i = tl.maximum(m_i, m_ij)\n",
    "            alpha = tl.exp(m_i - new_m_i)\n",
    "\n",
    "            exp_attn_score = tl.exp(attn_score - new_m_i[:, None])\n",
    "\n",
    "            l_i = l_i * alpha + tl.sum(exp_attn_score, axis=-1)\n",
    "            acc = acc * alpha[:, None]\n",
    "            # v就是compressed_kv，就是k的前kv_lora_rank维，也就是k_nope\n",
    "            acc = tl.dot(exp_attn_score.to(dtype), tl.trans(k_nope, 1,0), acc=acc)\n",
    "\n",
    "            m_i = new_m_i\n",
    "            k_nope_ptrs += BLOCK_SIZE_M * k_stride_m\n",
    "            k_rope_ptrs += BLOCK_SIZE_M * k_stride_m\n",
    "        acc = acc / l_i[:, None]\n",
    "\n",
    "        out_ptrs = OUT + nn[:, None] * out_stride_n + tl.arange(0, KV_LORA_RANK)[None, :]\n",
    "        tl.store(out_ptrs, acc.to(dtype), mask=mask_n[:, None])\n",
    "    else:\n",
    "        pass\n",
    "\n",
    "\n",
    "# @triton.autotune([triton.Config({'BLOCK_SIZE_M': bsn, 'BLOCK_SIZE_H': bsh}, num_stages=ns, num_warps=nw)\n",
    "#                  for bsn in [32, 64, 128]\n",
    "#                  for bsh in [16,32]\n",
    "#                  for ns in [1, 2, 4]\n",
    "#                  for nw in [4, 8]\n",
    "#                  ], key=['M', 'KV_LORA_RANK', 'ROPE_HEAD_DIM'])\n",
    "@triton.jit\n",
    "def _mla_decode_kernel(Q, K, V, OUT, \n",
    "                    NUM_PADS, SCALE,\n",
    "                    q_stride_b, q_stride_h, q_stride_n, q_stride_d,\n",
    "                    k_stride_b, k_stride_m, k_stride_d,\n",
    "                    v_stride_b, v_stride_m, v_stride_d,\n",
    "                    out_stride_b, out_stride_h, out_stride_n, out_stride_d,\n",
    "                    M, H, KV_LORA_RANK: tl.constexpr, ROPE_HEAD_DIM: tl.constexpr,\n",
    "                    BLOCK_SIZE_H: tl.constexpr, BLOCK_SIZE_M: tl.constexpr\n",
    "                    ):\n",
    "    off_b = tl.cast(tl.program_id(0), tl.int64)\n",
    "    off_h = tl.cast(tl.program_id(1), tl.int64) * BLOCK_SIZE_H\n",
    "\n",
    "    \n",
    "    NUM_PADS += off_b\n",
    "    num_pads = tl.cast(tl.load(NUM_PADS), tl.int64)\n",
    "\n",
    "    Q += off_b * q_stride_b + off_h * q_stride_h\n",
    "    K += off_b * k_stride_b + num_pads * k_stride_m\n",
    "    V += off_b * v_stride_b\n",
    "    OUT += off_b * out_stride_b + off_h * out_stride_h\n",
    "\n",
    "    hh = tl.arange(0, BLOCK_SIZE_H)\n",
    "    mm = tl.arange(0, BLOCK_SIZE_M)\n",
    "    \n",
    "    dtype = Q.type.element_ty\n",
    "\n",
    "    q_nope_ptrs = Q + hh[:, None] * q_stride_h + tl.arange(0, KV_LORA_RANK)[None, :]\n",
    "    q_rope_ptrs = Q + hh[:, None] * q_stride_h + tl.arange(0, ROPE_HEAD_DIM)[None, :] + KV_LORA_RANK\n",
    "    k_nope_ptrs = K + mm[None, :] * k_stride_m + tl.arange(0, KV_LORA_RANK)[:, None]\n",
    "    k_rope_ptrs = K + mm[None, :] * k_stride_m + tl.arange(0, ROPE_HEAD_DIM)[:, None] + KV_LORA_RANK\n",
    "    \n",
    "    mask_h = (off_h + hh) < H\n",
    "    q_nope = tl.load(q_nope_ptrs, mask=mask_h[:, None], other=0.)\n",
    "    q_rope = tl.load(q_rope_ptrs, mask=mask_h[:, None], other=0.)\n",
    "    acc = tl.zeros((BLOCK_SIZE_H, KV_LORA_RANK), dtype=tl.float32)\n",
    "    m_i = tl.zeros((BLOCK_SIZE_H,), dtype=tl.float32) - float('inf')\n",
    "    l_i = tl.zeros((BLOCK_SIZE_H,), dtype=tl.float32)\n",
    "    for start_m in range(num_pads, M, BLOCK_SIZE_M): \n",
    "        mask_m = (start_m + mm) < M\n",
    "        k_nope = tl.load(k_nope_ptrs, mask=mask_m[None, :], other=0.)\n",
    "        k_rope = tl.load(k_rope_ptrs, mask=mask_m[None, :], other=0.)\n",
    "\n",
    "        attn_score = tl.dot(q_rope, k_rope)\n",
    "        attn_score = tl.dot(q_nope, k_nope, acc=attn_score) * SCALE\n",
    "    \n",
    "        attn_score = tl.where(mask_m[None, :], attn_score, -65000)\n",
    "\n",
    "        m_ij = tl.max(attn_score, axis=1)\n",
    "        new_m_i = tl.maximum(m_i, m_ij)\n",
    "        alpha = tl.exp(m_i - new_m_i)\n",
    "\n",
    "        exp_attn_score = tl.exp(attn_score - new_m_i[:, None])\n",
    "\n",
    "        l_i = l_i * alpha + tl.sum(exp_attn_score, axis=-1)\n",
    "        acc = acc * alpha[:, None]\n",
    "\n",
    "        acc = tl.dot(exp_attn_score.to(dtype), tl.trans(k_nope, 1,0), acc=acc)\n",
    "\n",
    "        m_i = new_m_i\n",
    "        k_nope_ptrs += BLOCK_SIZE_M * k_stride_m\n",
    "        k_rope_ptrs += BLOCK_SIZE_M * k_stride_m\n",
    "    acc = acc / l_i[:, None]\n",
    "\n",
    "    out_ptrs = OUT + hh[:, None] * out_stride_h + tl.arange(0, KV_LORA_RANK)[None, :]\n",
    "    tl.store(out_ptrs, acc.to(dtype), mask=mask_h[:, None])\n",
    "\n",
    "# @triton.autotune([triton.Config({'BLOCK_SIZE_M': bsm, 'BLOCK_SIZE_H': bsh, 'BLOCK_SIZE_K': bsk,}, num_stages=ns, num_warps=nw)\n",
    "#                  for bsm in [32, 64, 128]\n",
    "#                  for bsh in [32]\n",
    "#                  for bsk in [32, 64, 128]\n",
    "#                  for ns in [2, 4]\n",
    "#                  for nw in [4, 8]\n",
    "#                  ], key=['M', 'KV_LORA_RANK', 'ROPE_HEAD_DIM'])\n",
    "@triton.jit\n",
    "def _stage1_compute_qk(Q, K, QK, \n",
    "                    NUM_PADS, SCALE,\n",
    "                    q_stride_b, q_stride_h, q_stride_n, q_stride_d,\n",
    "                    k_stride_b, k_stride_m, k_stride_d,\n",
    "                    M, H, KV_LORA_RANK: tl.constexpr, ROPE_HEAD_DIM: tl.constexpr,\n",
    "                    BLOCK_SIZE_H: tl.constexpr, BLOCK_SIZE_M: tl.constexpr, BLOCK_SIZE_K: tl.constexpr\n",
    "                    ):\n",
    "    off_b = tl.cast(tl.program_id(0), tl.int64)\n",
    "    off_h = tl.cast(tl.program_id(1), tl.int64) * BLOCK_SIZE_H\n",
    "    off_m = tl.cast(tl.program_id(2), tl.int64) * BLOCK_SIZE_M\n",
    "\n",
    "    \n",
    "    NUM_PADS += off_b\n",
    "    num_pads = tl.cast(tl.load(NUM_PADS), tl.int64)\n",
    "    if (off_m + BLOCK_SIZE_M) <= num_pads:\n",
    "        return\n",
    "\n",
    "    Q += off_b * q_stride_b + off_h * q_stride_h\n",
    "    K += off_b * k_stride_b + off_m * k_stride_m\n",
    "    QK += off_b * (M * H) + off_h * M + off_m\n",
    "    \n",
    "\n",
    "    hh = tl.arange(0, BLOCK_SIZE_H)\n",
    "    mm = tl.arange(0, BLOCK_SIZE_M)\n",
    "    kk = tl.arange(0, BLOCK_SIZE_K)\n",
    "    \n",
    "    dtype = Q.type.element_ty\n",
    "\n",
    "    q_nope_ptrs = Q + hh[:, None] * q_stride_h + kk[None, :]\n",
    "    q_rope_ptrs = Q + hh[:, None] * q_stride_h + tl.arange(0, ROPE_HEAD_DIM)[None, :] + KV_LORA_RANK\n",
    "    k_nope_ptrs = K + mm[None, :] * k_stride_m + kk[:, None]\n",
    "    k_rope_ptrs = K + mm[None, :] * k_stride_m + tl.arange(0, ROPE_HEAD_DIM)[:, None] + KV_LORA_RANK\n",
    "    \n",
    "    mask_h = (off_h + hh) < H\n",
    "    mask_m = (off_m + mm) < M\n",
    "\n",
    "    qk = tl.zeros((BLOCK_SIZE_H, BLOCK_SIZE_M), dtype=tl.float32)\n",
    "\n",
    "    q_rope = tl.load(q_rope_ptrs, mask=mask_h[:, None], other=0.)\n",
    "    k_rope = tl.load(k_rope_ptrs, mask=mask_m[None, :], other=0.)\n",
    "    qk = tl.dot(q_rope, k_rope, acc=qk)\n",
    "\n",
    "    for _ in range(0, KV_LORA_RANK, BLOCK_SIZE_K): \n",
    "        k_nope = tl.load(k_nope_ptrs, mask=mask_m[None, :], other=0.)\n",
    "        q_nope = tl.load(q_nope_ptrs, mask=mask_h[:, None], other=0.)\n",
    "        qk = tl.dot(q_nope, k_nope, acc=qk)\n",
    "        \n",
    "        k_nope_ptrs += BLOCK_SIZE_K\n",
    "        q_nope_ptrs += BLOCK_SIZE_K\n",
    "    qk = qk * SCALE\n",
    "    qk_ptrs = QK + hh[:, None] * M + mm[None, :]\n",
    "    tl.store(qk_ptrs, qk, mask=mask_h[:, None] & mask_m[None, :])\n",
    "\n",
    "# @triton.autotune([triton.Config({'BLOCK_SIZE_M': bsm, 'BLOCK_SIZE_H': bsh, 'BLOCK_SIZE_K': bsk,}, num_stages=ns, num_warps=nw)\n",
    "#                  for bsm in [32, 64, 128]\n",
    "#                  for bsh in [16]\n",
    "#                  for bsk in [64, 128, 256]\n",
    "#                  for ns in [2, 4]\n",
    "#                  for nw in [4, 8]\n",
    "#                  ], key=['M', 'KV_LORA_RANK', 'ROPE_HEAD_DIM'])\n",
    "@triton.jit\n",
    "def _stage2_compute_out(QK, V, OUT, \n",
    "                    NUM_PADS, \n",
    "                    v_stride_b, v_stride_m, v_stride_d,\n",
    "                    out_stride_b, out_stride_h, out_stride_n, out_stride_d,\n",
    "                    M, H, KV_LORA_RANK: tl.constexpr, ROPE_HEAD_DIM: tl.constexpr,\n",
    "                    BLOCK_SIZE_H: tl.constexpr, BLOCK_SIZE_M: tl.constexpr, BLOCK_SIZE_K: tl.constexpr,\n",
    "                    ):\n",
    "    off_b = tl.cast(tl.program_id(0), tl.int64)\n",
    "    off_h = tl.cast(tl.program_id(1), tl.int64) * BLOCK_SIZE_H\n",
    "    off_k = tl.cast(tl.program_id(2), tl.int64) * BLOCK_SIZE_K\n",
    "\n",
    "    \n",
    "    NUM_PADS += off_b\n",
    "    num_pads = tl.cast(tl.load(NUM_PADS), tl.int64)\n",
    "\n",
    "    QK += off_b * (M * H) + off_h * M + num_pads\n",
    "    V += off_b * v_stride_b + num_pads * v_stride_m + off_k\n",
    "    OUT += off_b * out_stride_b + off_h * out_stride_h + off_k\n",
    "\n",
    "    hh = tl.arange(0, BLOCK_SIZE_H)\n",
    "    mm = tl.arange(0, BLOCK_SIZE_M)\n",
    "    kk = tl.arange(0, BLOCK_SIZE_K)\n",
    "    \n",
    "    dtype = OUT.type.element_ty\n",
    "\n",
    "    qk_ptrs = QK + hh[:, None] * M + mm[None, :]\n",
    "    v_ptrs = V + mm[:, None] * v_stride_m + kk[None, :]\n",
    "    mask_h = (off_h + hh) < H\n",
    "    \n",
    "    acc = tl.zeros((BLOCK_SIZE_H, BLOCK_SIZE_K), dtype=tl.float32)\n",
    "    m_i = tl.zeros((BLOCK_SIZE_H,), dtype=tl.float32) - float('inf')\n",
    "    l_i = tl.zeros((BLOCK_SIZE_H,), dtype=tl.float32)\n",
    "    for start_m in range(num_pads, M, BLOCK_SIZE_M): \n",
    "        mask_m = (start_m + mm) < M\n",
    "        attn_score = tl.load(qk_ptrs, mask=mask_m[None, :] & mask_h[:, None], other=-65000)\n",
    "        \n",
    "        m_ij = tl.max(attn_score, axis=1)\n",
    "        new_m_i = tl.maximum(m_i, m_ij)\n",
    "        alpha = tl.exp(m_i - new_m_i)\n",
    "\n",
    "        exp_attn_score = tl.exp(attn_score - new_m_i[:, None])\n",
    "\n",
    "        l_i = l_i * alpha + tl.sum(exp_attn_score, axis=-1)\n",
    "        acc = acc * alpha[:, None]\n",
    "\n",
    "        v = tl.load(v_ptrs, mask=mask_m[:, None], other=0.) \n",
    "        # tl.static_print(exp_attn_score.shape, v.shape)\n",
    "        acc = tl.dot(exp_attn_score.to(dtype), v, acc=acc)\n",
    "\n",
    "        m_i = new_m_i\n",
    "        qk_ptrs += BLOCK_SIZE_M\n",
    "        v_ptrs += BLOCK_SIZE_M * v_stride_m\n",
    "    acc = acc / l_i[:, None]\n",
    "\n",
    "    out_ptrs = OUT + hh[:, None] * out_stride_h + kk[None, :]\n",
    "    tl.store(out_ptrs, acc, mask=mask_h[:, None])\n",
    "\n",
    "\n",
    "def triton_mqa(q:torch.Tensor, k:torch.Tensor, v:torch.Tensor, scale=None, attention_mask:torch.Tensor=None, tow_stage_decode=None):\n",
    "    \"\"\"\n",
    "    impl DeepSeek MLA by MQA with fast speed and low memory\n",
    "\n",
    "    Args:\n",
    "        q (Tensor): [bs, h, q_len, kv_lora_rank + rope_head_dim]\n",
    "        k (Tensor): [bs, 1, kv_len, kv_lora_rank + rope_head_dim]\n",
    "        v (Tensor): [bs, 1, kv_len, kv_lora_rank]\n",
    "        scale (Float): softmax scaling factor\n",
    "        attention (Tensor): [bs, kv_len], it's from the tokenizer(left_padding, 0 mean mask, 1 mean non-mask)\n",
    "        tow_stage_decode (Bool): Default None, it will auto choose use which method to decode, if set true use 2-stage, if set false use 1-stage\n",
    "    Return:\n",
    "        out: [bs, h, q_len, kv_lora_rank]\n",
    "    \"\"\"\n",
    "    B, H, N, D = q.shape\n",
    "    M = k.shape[-2]\n",
    "    # assert D == (256 + 32) or D == (512 + 64)\n",
    "    if D == (256 + 32):\n",
    "        KV_LORA_RANK = 256\n",
    "        ROPE_HEAD_DIM = 32\n",
    "    elif D == (512 + 64):\n",
    "        KV_LORA_RANK = 512\n",
    "        ROPE_HEAD_DIM = 64\n",
    "    else:\n",
    "        KV_LORA_RANK = 2**int(math.log2(D-1))\n",
    "        ROPE_HEAD_DIM = D - KV_LORA_RANK\n",
    "    assert math.log2(ROPE_HEAD_DIM).is_integer()\n",
    "    assert v.size(-1) == KV_LORA_RANK\n",
    "    assert N == 1 or M == N\n",
    "    assert scale is not None, 'must provide the softmax scale value'\n",
    "    # print(KV_LORA_RANK, ROPE_HEAD_DIM)\n",
    "    if k.dim() == 4:\n",
    "        k = k.squeeze(1)\n",
    "    if v.dim() == 4:\n",
    "        v = v.squeeze(1)\n",
    "\n",
    "    if attention_mask is not None:\n",
    "        num_pads = M - attention_mask.sum(-1)\n",
    "    else:\n",
    "        num_pads = torch.zeros((B,), dtype=torch.int32, device=q.device)\n",
    "    \n",
    "    out = torch.empty(B, H, N, KV_LORA_RANK, dtype=q.dtype, device=q.device)\n",
    "    if N > 1:\n",
    "        grids = lambda meta: (B, H, triton.cdiv(N, meta['BLOCK_SIZE_N']))\n",
    "        kwargs = {\"BLOCK_SIZE_N\": 64, \"BLOCK_SIZE_M\": 64, \"num_warps\": 8, \"num_stages\": 2}\n",
    "        _mla_encode_kernel[grids](q, k, v, out, \n",
    "                                num_pads, scale,\n",
    "                                *q.stride(),\n",
    "                                *k.stride(),\n",
    "                                *v.stride(),\n",
    "                                *out.stride(),\n",
    "                                N, KV_LORA_RANK, ROPE_HEAD_DIM,\n",
    "                                **kwargs\n",
    "                                )\n",
    "        \n",
    "    else:\n",
    "        if tow_stage_decode or (tow_stage_decode is None and B <= 4):\n",
    "            qk = torch.empty(B, H, M, dtype=torch.float32, device=q.device)\n",
    "            grids = lambda meta: (B, triton.cdiv(H, meta['BLOCK_SIZE_H']), triton.cdiv(M, meta['BLOCK_SIZE_M']))\n",
    "            kwargs = {\"BLOCK_SIZE_M\": 32, \"BLOCK_SIZE_H\": 32, \"BLOCK_SIZE_K\": 64, \"num_warps\": 4, \"num_stages\": 4}\n",
    "            _stage1_compute_qk[grids](q, k, qk, \n",
    "                                      num_pads, scale,\n",
    "                                      *q.stride(),\n",
    "                                      *k.stride(),\n",
    "                                      M, H, KV_LORA_RANK, ROPE_HEAD_DIM,\n",
    "                                      **kwargs\n",
    "                                      )\n",
    "            \n",
    "            grids = lambda meta: (B, triton.cdiv(H, meta['BLOCK_SIZE_H']), triton.cdiv(KV_LORA_RANK, meta['BLOCK_SIZE_K']))\n",
    "            kwargs = {\"BLOCK_SIZE_M\": 128, \"BLOCK_SIZE_H\": 16, \"BLOCK_SIZE_K\": 128, \"num_warps\": 8, \"num_stages\": 4}\n",
    "            _stage2_compute_out[grids](qk, v, out, \n",
    "                            num_pads,\n",
    "                            *v.stride(),\n",
    "                            *out.stride(),\n",
    "                            M, H, KV_LORA_RANK, ROPE_HEAD_DIM,\n",
    "                            **kwargs\n",
    "                            )\n",
    "        else:\n",
    "            grids = lambda meta: (B, triton.cdiv(H, meta['BLOCK_SIZE_H']))\n",
    "            kwargs = {\"BLOCK_SIZE_M\": 128, \"BLOCK_SIZE_H\": 16, \"num_warps\": 8, \"num_stages\": 2}\n",
    "            _mla_decode_kernel[grids](q, k, v, out, \n",
    "                                    num_pads, scale,\n",
    "                                    *q.stride(),\n",
    "                                    *k.stride(),\n",
    "                                    *v.stride(),\n",
    "                                    *out.stride(),\n",
    "                                    M, H, KV_LORA_RANK, ROPE_HEAD_DIM,\n",
    "                                    **kwargs\n",
    "                                    )\n",
    "    return out"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 精度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "torch.cuda.empty_cache()\n",
    "device = torch.device('cuda')\n",
    "dtype = torch.bfloat16\n",
    "bs, num_head, q_len, kv_len, rope_head_dim, nope_head_dim=16, 128, 1, 4096, 64, 128\n",
    "kv_lora_rank = 512\n",
    "scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "q = torch.randn(bs, num_head, q_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "k = torch.randn(bs, 1, kv_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "v = torch.randn(bs, 1, kv_len, kv_lora_rank, device=device, dtype=dtype)\n",
    "k[..., :kv_lora_rank] = v\n",
    "attention_mask = torch.ones(bs, kv_len, device=device, dtype=torch.int32)\n",
    "# attention_mask[:, :10] = 0\n",
    "attention_mask[:, :100] = 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch  bf16 vs torch fp32: 最大差值: 0.014720320701599121, 平均差值: 0.0006174223963171244\n",
      "triton bf16 vs torch fp32: 最大差值: 0.0038143396377563477, 平均差值: 0.00013092573499307036\n"
     ]
    }
   ],
   "source": [
    "a = triton_mqa(q, k, v, scale, attention_mask=attention_mask, tow_stage_decode=True)\n",
    "b = torch_mqa(q, k, v, scale, attention_mask=attention_mask)\n",
    "c = torch_mqa(q.float(), k.float(), v.float(), scale, attention_mask=attention_mask)\n",
    "if q_len > 1 and attention_mask is not None:\n",
    "    a.masked_fill_(~attention_mask.bool()[:, None, :, None], 0.)\n",
    "    b.masked_fill_(~attention_mask.bool()[:, None, :, None], 0.)\n",
    "    c.masked_fill_(~attention_mask.bool()[:, None, :, None], 0.)\n",
    "print(f'torch  bf16 vs torch fp32: 最大差值: {(c-b).abs().max().item()}, 平均差值: {(c-b).abs().mean().item()}')\n",
    "print(f'triton bf16 vs torch fp32: 最大差值: {(c-a).abs().max().item()}, 平均差值: {(c-a).abs().mean().item()}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.999922752380371\n",
      "0.2477847933769226\n"
     ]
    }
   ],
   "source": [
    "print(triton.testing.do_bench(lambda: torch_mqa(q, k, v, scale, attention_mask=attention_mask)))\n",
    "print(triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 速度"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## encode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "    kv_len       Triton         Torch\n",
      "0    128.0    70.777774    250.842720\n",
      "1    256.0   172.895983    574.897230\n",
      "2    384.0   312.487036   1063.505292\n",
      "3    512.0   490.819395   1608.174682\n",
      "4    640.0   712.210596   2443.881989\n",
      "5    768.0   970.037401   3180.700779\n",
      "6    896.0  1265.305042   4204.276562\n",
      "7   1024.0  1587.298632   5078.309059\n",
      "8   1152.0  1953.173280   8545.232773\n",
      "9   1280.0  2372.478724   9950.766563\n",
      "10  1408.0  2826.461077  11706.684113\n",
      "11  1536.0  3324.308872  13191.397667\n",
      "12  1664.0  3859.338284  15311.419487\n",
      "13  1792.0  4424.393654  17076.473236\n",
      "14  1920.0  5039.500237  19364.929199\n",
      "15  2048.0  5690.358639  21193.542480\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['kv_len'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[128 * i for i in range(1, 16+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'bs': 2, 'num_head': 128, 'rope_head_dim': 64, \n",
    "              'nope_head_dim': 128, 'kv_lora_rank': 512},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    device = torch.device('cuda')\n",
    "    dtype = torch.bfloat16\n",
    "    q_len = kv_len\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    q = torch.randn(bs, num_head, q_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    k = torch.randn(bs, 1, kv_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    v = torch.randn(bs, 1, kv_len, kv_lora_rank, device=device, dtype=dtype)\n",
    "    k[..., :kv_lora_rank] = v\n",
    "    attention_mask = torch.ones(bs, kv_len, device=device, dtype=torch.int32)\n",
    "    attention_mask = None\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## decode"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SM_COUNTS: 132\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjsAAAGwCAYAAABPSaTdAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAXLBJREFUeJzt3Xd4VGX+/vH3pIeEJCSQBgmETuidCCoLSBFdUbAtC1FBFjagiJXvurZV2R+r6+qK3QVUUFdXLCgg0lQIoXeIBAOhJaElk0LqnN8fYwYGAgkQcpLM/bquc83MOWfOfGacODfPec7zWAzDMBARERGpo9zMLkBERETkalLYERERkTpNYUdERETqNIUdERERqdMUdkRERKROU9gRERGROk1hR0REROo0D7MLqAlsNhtHjhyhfv36WCwWs8sRERGRSjAMg5ycHCIjI3Fzu3D7jcIOcOTIEaKioswuQ0RERC7DwYMHadKkyQW3K+wA9evXB+wfVkBAgMnViIiISGVYrVaioqIcv+MXorADjlNXAQEBCjsiIiK1TEVdUNRBWUREROo0hR0RERGp0xR2REREpE5Tn51KstlsFBUVmV2G1DKenp64u7ubXYaIiEtT2KmEoqIiUlNTsdlsZpcitVBQUBDh4eEaw0lExCQKOxUwDIOjR4/i7u5OVFTURQctEjmbYRjk5+eTmZkJQEREhMkViYi4JoWdCpSUlJCfn09kZCT16tUzuxypZXx9fQHIzMwkNDRUp7REREygZooKlJaWAuDl5WVyJVJblYXk4uJikysREXFNCjuVpP4Wcrn03RERMZfCjoiIiNRpCjsiIiJSpynsuLhnnnmGLl26mF2GiIjIVaOwU0f179+fqVOnVrjfI488wrJlyxyP77nnHkaMGHH1CjvHO++8Q//+/QkICMBisZCVlXVFx9u/fz8Wi4UtW7ZUSX0iInJlCkoKSDyYaGoNCjsuyjAMSkpK8Pf3JyQkxLQ68vPzGTp0KP/3f/9nWg0iIlK19p3cx+vrXmf4/OEE/79g+v6nL8fyjplWj8LOJTIMyMszZzGMytV4zz33sGrVKl599VUsFgsWi4U5c+ZgsVhYtGgR3bt3x9vbm59//tnpNNYzzzzD3Llz+eqrrxzPW7lyJQDbt29nwIAB+Pr6EhISwoQJE8jNzXV6zREjRvDSSy8RERFBSEgICQkJFV5uPXXqVJ544gn69OlT6f8Gp06dYvTo0TRq1AhfX19atWrF7NmzAYiJiQGga9euWCwW+vfvD8D69eu54YYbaNiwIYGBgVx//fVs2rTJ6bh79uyhX79++Pj4EBsbyw8//IDFYuHLL7907HPw4EHuuOMOgoKCCA4O5pZbbmH//v2Vrl1EpC46XXyaxSmLeXDRg7T+d2ta/rslUxZN4bu933G65DSR9SP59dSvptWnQQUvUX4++Pub89q5ueDnV/F+r776Kr/88gsdOnTgueeeA2Dnzp0APPHEE7z00ks0b96cBg0aOMIM2E9p7d69G6vV6ggPwcHB5OXlMWTIEOLi4li/fj2ZmZmMHz+eyZMnM2fOHMfzV6xYQUREBCtWrCAlJYU777yTLl26cP/991fZZwDw17/+lV27drFo0SIaNmxISkoKp0+fBmDdunX06tWLH374gfbt2zvGR8rJySE+Pp5///vfGIbByy+/zI033sjevXupX78+paWljBgxgujoaJKSksjJyeHhhx92et3i4mLH5/DTTz/h4eHB888/z9ChQ9m2bZvGYhIRl5JyMoVFexexKGURK/ev5HTJacc2DzcP+kX3Y1jLYQxrOYwOoR1MHYZDYacOCgwMxMvLi3r16hEeHg7YWy0AnnvuOW644YZyn+fv74+vry+FhYWO5wHMnTuXgoICPvjgA/x+S1uvv/46N998M//v//0/wsLCAGjQoAGvv/467u7utG3bluHDh7Ns2bIqDztpaWl07dqVHj16ANCsWTPHtkaNGgEQEhLi9B4GDBjgdIx33nmHoKAgVq1axU033cTSpUvZt28fK1eudDzvhRdecPqsPv30U2w2G++9957jj3b27NkEBQWxcuVKBg8eXKXvU0SkJjldfJqV+1eyKMUecFJOpjhtbxLQxBFuBjYfSIB3gEmVnk9h5xLVq2dvYTHrta9UWUC4FLt376Zz586OoAPQt29fbDYbycnJjrDTvn17p+kQIiIi2L59OwAvvvgiL774omPbrl27iI6OrvC1hw0bxk8//QRA06ZN2blzJ5MmTWLkyJFs2rSJwYMHM2LECK655pqLHicjI4Mnn3ySlStXkpmZSWlpKfn5+aSlpQGQnJxMVFSUU0Dq1auX0zG2bt1KSkoK9evXd1pfUFDAvn37KnwvIiK1jbXQysJfFvL5rs9ZnLL4vNab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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "      bs  triton-1-stage  triton-2-stage\n",
      "0    1.0      175.136000       85.100219\n",
      "1    2.0      171.488166       93.088821\n",
      "2    3.0      170.733750       96.734338\n",
      "3    4.0      172.049060      102.462463\n",
      "4    5.0      173.995927      179.080278\n",
      "5    6.0      177.860916      184.999406\n",
      "6    7.0      181.394160      193.034321\n",
      "7    8.0      183.105052      196.778923\n",
      "8    9.0      183.797300      269.268632\n",
      "9   10.0      185.751140      275.304615\n",
      "10  11.0      187.657520      281.383067\n",
      "11  12.0      189.283058      288.441569\n",
      "12  13.0      189.428121      357.932627\n",
      "13  14.0      189.736441      364.126265\n",
      "14  15.0      189.839348      370.229959\n",
      "15  16.0      191.602260      378.218055\n",
      "16  17.0      346.373200      447.759569\n",
      "17  18.0      354.056716      455.698699\n",
      "18  19.0      354.182452      460.142523\n",
      "19  20.0      354.196489      472.486734\n",
      "20  21.0      354.809910      535.745502\n",
      "21  22.0      359.655261      540.527344\n",
      "22  23.0      356.523186      553.407967\n",
      "23  24.0      360.066652      554.228961\n",
      "24  25.0      365.398377      626.755416\n",
      "25  26.0      365.834177      633.122444\n",
      "26  27.0      370.037347      635.074973\n",
      "27  28.0      370.873779      646.836460\n",
      "28  29.0      372.451097      709.615648\n",
      "29  30.0      375.395536      718.493998\n",
      "30  31.0      378.066659      727.766633\n",
      "31  32.0      376.680166      735.799134\n"
     ]
    }
   ],
   "source": [
    "sm_counts = torch.cuda.get_device_properties('cuda').multi_processor_count\n",
    "print(f'SM_COUNTS: {sm_counts}')\n",
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['bs'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[i for i in range(1, 32+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['1-stage', '2-stage'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"triton-1-stage\",\n",
    "            \"triton-2-stage\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'kv_len': 4096, 'num_head': 128, 'rope_head_dim': 64, \n",
    "              'nope_head_dim': 128, 'kv_lora_rank': 512},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    device = torch.device('cuda')\n",
    "    dtype = torch.bfloat16\n",
    "    q_len = 1\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    q = torch.randn(bs, num_head, q_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    k = torch.randn(bs, 1, kv_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    v = torch.randn(bs, 1, kv_len, kv_lora_rank, device=device, dtype=dtype)\n",
    "    k[..., :kv_lora_rank] = v\n",
    "    attention_mask = torch.ones(bs, kv_len, device=device, dtype=torch.int32)\n",
    "    attention_mask = None\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == '1-stage':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask, tow_stage_decode=False))\n",
    "    if provider == '2-stage':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask, tow_stage_decode=True))\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "      bs      triton         Torch\n",
      "0    1.0  176.043838    215.424657\n",
      "1    2.0  171.237215   5192.801952\n",
      "2    3.0  170.572832   7796.260834\n",
      "3    4.0  172.148675  10311.325073\n",
      "4    5.0  174.306512  12871.994972\n",
      "5    6.0  178.015158  15439.877510\n",
      "6    7.0  181.726381  17989.612579\n",
      "7    8.0  183.423296  20577.903748\n",
      "8    9.0  183.644727  23159.799576\n",
      "9   10.0  186.694115  25723.711014\n",
      "10  11.0  188.749000  28364.608765\n",
      "11  12.0  190.305620  30920.587540\n",
      "12  13.0  189.450711  33391.887665\n",
      "13  14.0  191.128299  36076.850891\n",
      "14  15.0  191.198498  38526.878357\n",
      "15  16.0  191.305920  41523.166656\n",
      "16  17.0  348.864406  43828.544617\n",
      "17  18.0  352.297246  46296.928406\n",
      "18  19.0  355.049968  48853.534698\n",
      "19  20.0  353.810757  51512.737274\n",
      "20  21.0  354.580879  53994.174957\n",
      "21  22.0  357.973009  57215.934753\n",
      "22  23.0  356.640041  59109.729767\n",
      "23  24.0  361.275852  61713.375092\n",
      "24  25.0  362.815708  64308.738708\n",
      "25  26.0  365.141362  66779.838562\n",
      "26  27.0  364.828408  69429.153442\n",
      "27  28.0  370.500743  72066.787720\n",
      "28  29.0  369.811296  74426.017761\n",
      "29  30.0  374.243766  77190.849304\n",
      "30  31.0  375.876218  79735.519409\n",
      "31  32.0  370.828241  82307.426453\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['bs'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[i for i in range(1, 32+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'kv_len': 4096, 'num_head': 128, 'rope_head_dim': 64, \n",
    "              'nope_head_dim': 128, 'kv_lora_rank': 512},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    device = torch.device('cuda')\n",
    "    dtype = torch.bfloat16\n",
    "    q_len = 1\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    q = torch.randn(bs, num_head, q_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    k = torch.randn(bs, 1, kv_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    v = torch.randn(bs, 1, kv_len, kv_lora_rank, device=device, dtype=dtype)\n",
    "    k[..., :kv_lora_rank] = v\n",
    "    attention_mask = torch.ones(bs, kv_len, device=device, dtype=torch.int32)\n",
    "    attention_mask = None\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "    kv_len      triton         Torch\n",
      "0    512.0   35.938960   5071.476460\n",
      "1   1024.0   60.133755  10475.356102\n",
      "2   1536.0   83.111539  15558.148384\n",
      "3   2048.0  104.651019  20764.736176\n",
      "4   2560.0  125.070930  25820.671082\n",
      "5   3072.0  147.124171  30834.890366\n",
      "6   3584.0  167.608932  36103.088379\n",
      "7   4096.0  192.112491  41248.401642\n",
      "8   4608.0  207.200646  46277.503967\n",
      "9   5120.0  227.741569  51537.822723\n",
      "10  5632.0  249.469474  56429.279327\n",
      "11  6144.0  268.690258  61461.791992\n",
      "12  6656.0  290.033758  66552.543640\n",
      "13  7168.0  308.453739  71489.440918\n",
      "14  7680.0  327.018708  76727.836609\n",
      "15  8192.0  347.484052  81739.807129\n"
     ]
    }
   ],
   "source": [
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['kv_len'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[512 * i for i in range(1, 16+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"triton\",\n",
    "            \"Torch\",\n",
    "            \n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'bs': 16, 'num_head': 128, 'rope_head_dim': 64, \n",
    "              'nope_head_dim': 128, 'kv_lora_rank': 512},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    device = torch.device('cuda')\n",
    "    dtype = torch.bfloat16\n",
    "    q_len = 1\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    q = torch.randn(bs, num_head, q_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    k = torch.randn(bs, 1, kv_len, kv_lora_rank+rope_head_dim, device=device, dtype=dtype)\n",
    "    v = torch.randn(bs, 1, kv_len, kv_lora_rank, device=device, dtype=dtype)\n",
    "    k[..., :kv_lora_rank] = v\n",
    "    attention_mask = torch.ones(bs, kv_len, device=device, dtype=torch.int32)\n",
    "    attention_mask = None\n",
    "\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mqa(q, k, v, scale, attention_mask=attention_mask))\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
